<?php
/*
 * Mysqli 数据库核心函数
 * @package    system
 * @author     Mark
 * @copyright  Copyright (c) 2013 ixdcw team
 * @version    $Id: a3197cd2c3f865086356317b085eaa5d59ec78d6 $
 */
defined('IN_IXDCW') or exit('Access Denied');
class DbMysqli {

	/** @var  链接ID */
	public $connid;

	/** @var  数据表前缀 */
	public $pre;

	/** @var int 查询数量 */
	public $querynum = 0;

	/** @var string 当前的数据库名称 */
	public $dbName = '';

	/** @var  缓存时间 */
	public $ttl;

	/** @var int 查询指针 */
	public $cursor = 0;

	/** @var string 缓存ID */
	public $cache_id = '';

	/** @var string 缓存时间 */
	public $cache_ttl = '';

	/** @var int 是否数据提示 */
	public $halt = 0;

	/** @var int 位置 */
	public $cids = 0;

	/** @var int 是否链接状态 */
	public $linked = 1;

	/** @var array 结果集 */
	public $result = array();

	private $_records = array();

	/** @var array 所有的缓存ID */
	public $cache_ids = array();

	/**
	 * 链接数据库
	 * @param     $dbhost
	 * @param     $dbuser
	 * @param     $dbpass
	 * @param     $dbname
	 * @param     $dbttl
	 * @param     $dbcharset
	 * @param int $pconnect
	 * @return mysqli
	 */
	function connect($dbhost, $dbuser, $dbpass, $dbname, $dbttl, $dbcharset, $pconnect = 0) {
		$this->ttl = $dbttl;
		$this->dbName = $dbname;
		@list($dbhost, $dbport) = explode(':', $dbhost);
		$dbport or $dbport = 3306;
		$this->connid = mysqli_init();
		if (mysqli_real_connect($this->connid, $dbhost, $dbuser, $dbpass, false, $dbport)) {
			//
		} else {
			$this->linked = 0;
			$retry        = 5;
			while ($retry-- > 0) {
				if (mysqli_real_connect($this->connid, $dbhost, $dbuser, $dbpass, false, $dbport)) {
					$this->linked = 1;
					break;
				}
			}
			if ($this->linked == 0) {
				if ($this->halt) {
					exit(include template('mysql', 'message'));
				} else {
					$this->halt('Can not connect to MySQL server');
				}
			}
		}
		$version = $this->version();
		if ($version > '4.1' && $dbcharset) mysqli_query($this->connid, "SET NAMES '" . $dbcharset . "'");
		if ($version > '5.0') mysqli_query($this->connid, "SET sql_mode=''");
		if ($dbname && !$this->selectDb($dbname)) $this->halt('Cannot use database ' . $dbname);
		return $this->connid;
	}


	/**
	 * @param $dbname
	 * @return bool
	 */
	function selectDb($dbname) {
		return mysqli_select_db($this->connid, $dbname);
	}


	/**
	 * 获取多条数据
	 * @param  string  $sql  查询字串
	 * @param  string  $type 缓存类型
	 * @param  integer $ttl  缓存的时间.
	 * @return array        返回查询到的数组
	 */

	function getAll($sql, $type = '', $ttl = 0) {
		$query  = $this->query($sql, $type, $ttl);
		$return = array();
		while ($r = $this->fetchArray($query)) {
			$return[] = $r;
		}
		$this->freeResult($query);
		return $return;
	}

	/**
	 * 获取一列数据
	 * @param null $query
	 * @param int  $x
	 * @return array
	 */
	function getCol($query = null, $x = 0) {

		// If there is a query then perform it if not then use cached results..
		$results   = $this->getAll($query);
		$new_array = array();
		// Extract the column values
		foreach ($results as $v) {
			$new_array[] = $v[$x];
		}

		return $new_array;
	}

	/**
	 * 获取单个变量的值
	 * @param        $sql
	 * @param string $type
	 * @param int    $ttl
	 * @return null 获取单个变量的值,如果没有 返回null
	 */
	function getVar($sql, $type = '', $ttl = 0) {
		$query = $this->query($sql, $type, $ttl);
		$r     = $this->fetchArray($query);
		if ($r) {
			$values = array_values($r);
			return (isset($values[0]) && $values[0] !== '') ? $values[0] : null;
		} else {
			return null;
		}
	}

	/**
	 * 查询值
	 * @param        $sql
	 * @param string $type
	 * @param int    $ttl
	 * @param bool   $save_id
	 * @return array|bool|mixed|mysqli_result|string
	 */
	function query($sql, $type = '', $ttl = 0, $save_id = false) {
		if (DT_DEBUG || XD_DEBUG) {
			timer_start();
		}

		$select = strtoupper(substr($sql, 0, 7)) == 'SELECT ' ? 1 : 0;
		if ($this->ttl > 0 && $type == 'CACHE' && $select) {
			$this->cursor   = 0;
			$this->cache_id = md5($sql);
			if ($this->cids) $this->cache_ids[] = $this->cache_id;
			$this->result    = array();
			$this->cache_ttl = ($ttl ? $ttl : $this->ttl) + mt_rand(-10, 30);
			return $this->_query($sql);
		}
		if (!$save_id) $this->cache_id = 0;
		if (!($query = mysqli_query($this->connid, $sql))) $this->halt('MySQL Query Error', $sql);
		$this->querynum++;
		if (DT_DEBUG || XD_DEBUG) {
			$time = timer_end();
			$this->_record($sql, $time);
		}
		return $query;
	}

	/**
	 *
	 * @param        $sql
	 * @param string $type
	 * @param int    $ttl
	 * @return array|null
	 */
	function get_one($sql, $type = '', $ttl = 0) {
		$sql = str_replace(array('select ', ' limit '), array('SELECT ', ' LIMIT '), $sql);

		if (strpos($sql, 'SELECT ') !== false && strpos($sql, ' LIMIT ') === false) $sql .= ' LIMIT 0,1';

		$query = $this->query($sql, $type, $ttl);

		$r     = $this->fetchArray($query);
		$this->freeResult($query);

		return $r;

	}

	/**
	 * 获取一条数据
	 * @param        $sql
	 * @param string $type
	 * @param int    $ttl
	 * @return array|null
	 */
	public function getOne($sql, $type = '', $ttl = 0) {
       // var_dump($this->get_one($sql, $type, $ttl));
		return $this->get_one($sql, $type, $ttl);
	}

	/**
	 * 统计数据表中的数据
	 * @param        $table
	 * @param string $condition
	 * @param int    $ttl
	 * @return int
	 */
	function count($table, $condition = '', $ttl = 0) {
		$sql = 'SELECT COUNT(*) as amount FROM ' . $table;
		if ($condition) $sql .= ' WHERE ' . $condition;
		$r = $this->getOne($sql, $ttl ? 'CACHE' : '', $ttl);
		return $r ? intval($r['amount']) : 0;
	}

	/**
	 * 等同于PHP的获取单条数据信息
	 * @param     $query
	 * @param int $result_type
	 * @return array|null
	 */
	function fetchArray($query, $result_type = MYSQL_ASSOC) {
		return $this->cache_id ? $this->_fetch_array($query) : mysqli_fetch_array($query, $result_type);
	}

	/**
	 * 获取影响到的条目
	 * @return int
	 */
	function affectedRows() {
		return mysqli_affected_rows($this->connid);
	}

	/**
	 * 影响到的条目
	 * @param $query
	 * @return int
	 */
	function numRows($query) {
		return mysqli_num_rows($query);
	}

	/**
	 * 字段的数量
	 * @param $query
	 * @return int
	 */
	function numFields($query) {
		return mysqli_num_fields($query);
	}

	/**
	 * @param $query
	 * @param $row
	 * @return mixed
	 */
	function result($query, $row) { //DEBUG
		return @mysqli_result($query, $row);
	}

	/**
	 * @param $query
	 * @see freeResult
	 */
	function freeResult($query) {
		return @mysqli_free_result($query);
	}

	/**
	 * @return int|string
	 */
	function insertId() {
		return mysqli_insert_id($this->connid);
	}

	function fetchRow($query) {
		return mysqli_fetch_row($query);
	}

	function version() {
		return mysqli_get_server_info($this->connid);
	}

	function close() {
		return mysqli_close($this->connid);
	}

	function error() {
		return @mysqli_error($this->connid);
	}

	function errno() {
		return intval($this->error());
	}

	/**
	 * 显示提示信息
	 * @param string $message
	 * @param string $sql
	 */
	function halt($message = '', $sql = '') {
		if ($message && DT_DEBUG) log_write("\t\t<query>" . $sql . "</query>\n\t\t<errno>" . $this->errno() . "</errno>\n\t\t<error>" . $this->error() . "</error>\n\t\t<errmsg>" . $message . "</errmsg>\n", 'sql');
		if ($this->halt) Prompt::message('MySQL Query:' . $sql . ' <br/> MySQL Error:' . $this->error() . ' MySQL Errno:' . $this->errno() . ' <br/>Message:' . $message);
	}

	/**
	 * 返回日志记录
	 * @return array
	 */
	public function getRecords() {
		return $this->_records;
	}

	/**
	 * 记录debug日志
	 * @param $sql
	 * @param $time
	 */
	private function _record($sql, $time){
		$this->_records[] = array(
			'time' => $time,
			'sql'  => $sql,
		);
	}

	private function _query($sql) {
		global $dc;
		$this->result = $dc->get($this->cache_id);
		if (!$this->result) {
			$tmp    = array();
			$result = $this->query($sql, '', '', true);
			while ($r = mysqli_fetch_array($result, MYSQL_ASSOC)) {
				$tmp[] = $r;
			}
			$this->result = $tmp;
			$this->freeResult($result);
			$dc->set($this->cache_id, $tmp, $this->cache_ttl);
		}
		return $this->result;
	}

	function _fetch_array($query = array()) {
		if ($query) $this->result = $query;
		if (isset($this->result[$this->cursor])) {
			return $this->result[$this->cursor++];
		} else {
			$this->cursor = $this->cache_id = 0;
			return array();
		}
	}

	public function setPre($pre) {
		$this->pre = $pre;
	}
	/**
	 * 获取前缀
	 * @return mixed
	 */
	public function getPre() {
		return $this->pre;
	}
}